﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Networking
{
    public enum PacketType
    {
        AckNack,
        ProtocolVersion,
        ProtocolVersionResponse,
        Security,
        Autentication,
        ClientInit,
        ServerInit,
        SetPixelFormat,
        FramebufferUpdateRequest,
        KeyEvent,
        PointerEvent,
        FramebufferUpdateResponse,
        SecurityResponse,
        AutenticationResponse
    };

    public enum SecurityType
    {
        None = 0,
        //
    }

    public static class PacketFactory
    {

        /// <summary>
        /// Parses a byte buffer extracting the provided packet type
        /// </summary>
        /// <param name="Type">The packet type to extract</param>
        /// <param name="Buffer">The byte buffer containing the packet</param>
        /// <returns></returns>
        public static Packet ParsePacket(PacketType Type, byte[] Buffer)
        {
            switch (Type)
            {
                case PacketType.AckNack:
                case PacketType.ProtocolVersionResponse:

                    return new AckNackPacket(Buffer);                                        

                case PacketType.Autentication:

                    return new AutenticationPacket(Buffer);

                case PacketType.AutenticationResponse:

                    return new AutenticationPacket(Buffer);

                case PacketType.ClientInit:

                    return new ClientInitPacket(Buffer);

                case PacketType.FramebufferUpdateRequest:

                    return new FramebufferUpdateRequestPacket(Buffer);

                case PacketType.FramebufferUpdateResponse:

                    return new FramebufferUpdateResponsePacket(Buffer);

                case PacketType.KeyEvent:

                    return new KeyEventPacket(Buffer);

                case PacketType.PointerEvent:

                    return new PointerEventPacket(Buffer);

                case PacketType.ProtocolVersion:

                    return new ProtocolVersionPacket(Buffer);

                case PacketType.Security:

                    return new SecurityPacket(Buffer);

                case PacketType.SecurityResponse:

                    return new SecurityResponsePacket(Buffer);
                    
                case PacketType.ServerInit:

                    return new ServerInitPacket(Buffer);

                case PacketType.SetPixelFormat:

                    return new SetPixelFormatPacket(Buffer);

            }

            return null;
        

        }

        /// <summary>
        /// Creates a new packet specifying the packet type and the packet parameters
        /// </summary>
        /// <param name="Type">The packet type to create</param>
        /// <param name="Parameters">The packet parameters (vary on packet type)</param>
        /// <returns>The packet of the provided type</returns>
        public static Packet CreatePacket(PacketType Type, params object[] Parameters) {

            switch (Type)
            {
                case PacketType.AckNack:
                case PacketType.ProtocolVersionResponse:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.AckNack);

                    if (Parameters[0] is bool) {

                        return new AckNackPacket((bool)Parameters[0]);

                    }

                    if (Parameters[0] is byte[])
                    {
                        return new AckNackPacket((byte[])Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.AckNack);
                                        

                case PacketType.Autentication:

                    if (Parameters.Count() > 1) throw new InvalidPacketParametersException(PacketType.Autentication);

                    if (Parameters.Count() == 0)
                    {
                        return new AutenticationPacket();
                    }

                    if (Parameters[0] is int)
                    {
                        return new AutenticationPacket((int)Parameters[0]);
                    }

                    if (Parameters[0] is byte[])
                    {
                        return new AutenticationPacket((byte[])Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.Autentication);


                case PacketType.AutenticationResponse:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.AutenticationResponse);

                    if (Parameters[0] is int)
                    {
                        return new AutenticationResponsePacket((int)Parameters[0]);
                    }

                    if (Parameters[0] is byte[])
                    {
                        return new AutenticationResponsePacket((byte[])Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.AutenticationResponse);


                case PacketType.ClientInit:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.ClientInit);

                    if (Parameters[0] is bool)
                    {
                        return new ClientInitPacket((bool)Parameters[0]);
                    }

                    if (Parameters[0] is byte[])
                    {
                        return new ClientInitPacket((byte[])Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.ClientInit);


                case PacketType.FramebufferUpdateRequest:

                    if (Parameters.Count() != 3) throw new InvalidPacketParametersException(PacketType.FramebufferUpdateRequest);

                    if (Parameters[0] is byte &&
                        Parameters[1] is Coords &&
                        Parameters[2] is Size)
                    {
                        return new FramebufferUpdateRequestPacket((byte)Parameters[0], (Coords)Parameters[1], (Size)Parameters[2]);
                    }

                    throw new InvalidPacketParametersException(PacketType.FramebufferUpdateRequest);


                case PacketType.FramebufferUpdateResponse:

                    if (Parameters.Count() == 1 && 
                       (Parameters[0] is RectangleInfo[]))
                    {
                        return new FramebufferUpdateResponsePacket((RectangleInfo[])Parameters[0]);
                    }

                    if (Parameters.All(param => param is RectangleInfo))
                    {
                        return new FramebufferUpdateResponsePacket((RectangleInfo[])Parameters);
                    }

                    throw new InvalidPacketParametersException(PacketType.FramebufferUpdateResponse);

                case PacketType.KeyEvent:

                    if (Parameters.Count() != 2) throw new InvalidPacketParametersException(PacketType.KeyEvent);

                    if (Parameters[0] is bool &&
                        Parameters[1] is Keys)
                    {
                        return new KeyEventPacket((bool)Parameters[0], (Keys)Parameters[1]);
                    }

                    throw new InvalidPacketParametersException(PacketType.KeyEvent);


                case PacketType.PointerEvent:

                    if (Parameters.Count() != 6) throw new InvalidPacketParametersException(PacketType.PointerEvent);

                    if (Parameters[0] is bool &&
                        Parameters[1] is bool &&
                        Parameters[2] is bool &&
                        Parameters[3] is bool &&
                        Parameters[4] is bool &&
                        Parameters[5] is Coords)
                    {

                        return new PointerEventPacket(  (bool)Parameters[0],
                                                        (bool)Parameters[1],
                                                        (bool)Parameters[2],
                                                        (bool)Parameters[3],
                                                        (bool)Parameters[4],
                                                        (Coords)Parameters[5]);

                    }

                    throw new InvalidPacketParametersException(PacketType.PointerEvent);


                case PacketType.ProtocolVersion:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.ProtocolVersion);

                    if (Parameters[0] is string)
                    {
                        return new ProtocolVersionPacket((string)Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.ProtocolVersion);


                case PacketType.Security:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.Security);

                    if (Parameters[0] is SecurityType[])
                    {
                        return new SecurityPacket((SecurityType[])Parameters[0]);
                    }

                    if (Parameters.All(param => param is SecurityPacket))
                    {

                        List<SecurityType> lstTemp = new List<SecurityType>(Parameters.Count());

                        foreach (object type in Parameters)
                        {
                            lstTemp.Add((SecurityType) type);    
                        }

                        return new SecurityPacket(lstTemp.ToArray());

                    }

                    throw new InvalidPacketParametersException(PacketType.Security);


                case PacketType.SecurityResponse:


                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.SecurityResponse);

                    if (Parameters[0] is SecurityType)
                    {
                        return new SecurityResponsePacket((SecurityType)Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.SecurityResponse);


                case PacketType.ServerInit:

                    if (Parameters.Count() != 3) throw new InvalidPacketParametersException(PacketType.ServerInit);

                    if (Parameters[0] is PixelFormat &&
                        Parameters[1] is Size &&
                        Parameters[2] is string)
                    {

                        return new ServerInitPacket(    (PixelFormat)Parameters[0],
                                                        (Size)Parameters[1],
                                                        (string)Parameters[2]);

                    }

                    throw new InvalidPacketParametersException(PacketType.ServerInit);


                case PacketType.SetPixelFormat:

                    if (Parameters.Count() != 1) throw new InvalidPacketParametersException(PacketType.SetPixelFormat);

                    if (Parameters[0] is PixelFormat)
                    {
                        return new SetPixelFormatPacket((PixelFormat)Parameters[0]);
                    }

                    throw new InvalidPacketParametersException(PacketType.SetPixelFormat);


            }

            return null;
        }



    }



}
